# -*- coding: utf-8 -*-
import os
import re
import hmac
import json
import time
from typing import Union
import wave  # 使用Python内置的wave模块，无需额外安装
import base64
import random
import string
import datetime
import warnings
import urllib.parse

import requests
from loguru import logger

try:
    from configs import LFASR_HOST, API_UPLOAD, API_GET_RESULT, XFYUN_APPID, XFYUN_ACCESS_KEY_ID, XFYUN_ACCESS_KEY_SECRET
except ImportError:
    LFASR_HOST = "https://office-api-ist-dx.iflyaisol.com"
    API_UPLOAD = "/v2/upload"
    API_GET_RESULT = "/v2/getResult"
    XFYUN_APPID = "32f35bca"
    XFYUN_ACCESS_KEY_ID = "843d0d96b7c122ca9158f0393729a480"
    XFYUN_ACCESS_KEY_SECRET = "ZDY0OWJkM2Q4MTU4OTNjNTQwNjQ2ZjVm"

# 忽略SSL验证警告（生产环境建议开启验证）
warnings.filterwarnings("ignore", category=requests.packages.urllib3.exceptions.InsecureRequestWarning)


class XfyunAsrClient:
    def __init__(self):
        self.appid = XFYUN_APPID
        self.access_key_id = XFYUN_ACCESS_KEY_ID
        self.access_key_secret = XFYUN_ACCESS_KEY_SECRET
        self.lfasr_host = LFASR_HOST
        self.api_upload = API_UPLOAD
        self.api_get_result = API_GET_RESULT
        self.signature_random = self._generate_random_str()

        self.last_base_string = ""  # 签名原始串（编码后）
        self.last_signature = ""    # 最终签名
        self.upload_url = ""        # 最终生成的请求URL

    # 解析API响应，提取所有w字段内容并拼接
    @staticmethod
    def parse_order_result(api_response) -> str:
        """
        解析完整的API响应，提取orderResult中的所有w字段内容并拼接
        
        参数:
            api_response: 完整的API响应字典
        返回:
            拼接后的文本字符串
        """
        try:
            # 从API响应中获取orderResult字段
            order_result_str = api_response.get('content', {}).get('orderResult', '{}')
            
            # 处理转义字符问题
            cleaned_str = re.sub(r'\\\\', r'\\', order_result_str)
            
            # 解析orderResult字符串为JSON对象
            order_result = json.loads(cleaned_str)
            
            # 提取所有w字段的值
            w_values = []
            
            # 遍历lattice数组
            if 'lattice' in order_result:
                for lattice_item in order_result['lattice']:
                    if 'json_1best' in lattice_item:
                        # 解析json_1best字段
                        json_1best = json.loads(lattice_item['json_1best'])
                        
                        # 处理st对象
                        if 'st' in json_1best and 'rt' in json_1best['st']:
                            for rt_item in json_1best['st']['rt']:
                                if 'ws' in rt_item:
                                    for ws_item in rt_item['ws']:
                                        if 'cw' in ws_item:
                                            for cw_item in ws_item['cw']:
                                                if 'w' in cw_item:
                                                    w_values.append(cw_item['w'])
        
            # 拼接所有w值
            return ''.join(w_values)
            
        except json.JSONDecodeError as e:
            logger.error(f"JSON解析错误: {e}")
            return ""
        except Exception as e:
            logger.error(f"处理过程中出错: {e}")
            return ""

    def _check_audio_path(self, path):
        if not os.path.exists(path):
            raise FileNotFoundError(f"音频文件不存在：{path}")
        # 校验是否为WAV文件
        if not path.lower().endswith(".wav"):
            raise ValueError(f"当前代码仅支持WAV格式音频，您的文件格式为：{os.path.splitext(path)[1]}")
        return os.path.abspath(path)

    def _generate_random_str(self, length=16):
        return ''.join(random.choices(string.ascii_letters + string.digits, k=length))

    def _get_local_time_with_tz(self):
        """生成带时区偏移的本地时间（格式：yyyy-MM-dd'T'HH:mm:ss±HHmm）"""
        local_now = datetime.datetime.now()
        tz_offset = local_now.astimezone().strftime('%z')  # 输出格式：+0800 或 -0500
        return f"{local_now.strftime('%Y-%m-%dT%H:%M:%S')}{tz_offset}"

    def _get_wav_duration_ms(self, audio_file_path: str):
        """
        用Python内置wave模块获取WAV音频时长（毫秒，整数）
        原理：时长(毫秒) = 总帧数 / 采样率 * 1000
        """
        try:
            with wave.open(audio_file_path, 'rb') as wav_file:
                # 获取WAV文件关键参数：nframes=总帧数，framerate=采样率（Hz）
                n_frames = wav_file.getnframes()
                sample_rate = wav_file.getframerate()
                
                # 计算时长（毫秒），转换为整数
                duration_ms = int(round(n_frames / sample_rate * 1000))
                return duration_ms
        except wave.Error as e:
            raise Exception(f"WAV文件解析失败：{str(e)}，请确认文件为标准WAV格式（非损坏、非压缩）")
        except Exception as e:
            raise Exception(f"获取音频时长失败：{str(e)}")

    def generate_signature(self, params):
        """生成签名（根据文档要求：对key和value都进行url encode后生成baseString）"""
        # 排除signature参数，按参数名自然排序（与Java TreeMap一致）
        sign_params = {k: v for k, v in params.items() if k != "signature"}
        sorted_params = sorted(sign_params.items(), key=lambda x: x[0])
        
        # 构建baseString：对key和value都进行URL编码
        base_parts = []
        for k, v in sorted_params:
            if v is not None and str(v).strip() != "":
                encoded_key = urllib.parse.quote(k, safe='')  # 参数名编码
                encoded_value = urllib.parse.quote(str(v), safe='')  # 参数值编码
                base_parts.append(f"{encoded_key}={encoded_value}")
        
        self.last_base_string = "&".join(base_parts)
        
        # HMAC-SHA1加密 + Base64编码
        hmac_obj = hmac.new(
            self.access_key_secret.encode("utf-8"),
            self.last_base_string.encode("utf-8"),
            digestmod="sha1"
        )
        self.last_signature = base64.b64encode(hmac_obj.digest()).decode("utf-8")
        return self.last_signature

    # 上传音频，转写音频，返回订单ID
    def upload_audio(self, audio_file_path: str):
        # 1. 基础参数准备（duration字段为毫秒整数）
        audio_file_path = self._check_audio_path(audio_file_path)
        audio_duration = self._get_wav_duration_ms(audio_file_path)
        audio_size = str(os.path.getsize(audio_file_path))  # 音频文件大小（字节）
        audio_name = os.path.basename(audio_file_path)      # 音频文件名
        date_time = self._get_local_time_with_tz()               # 带时区的本地时间
        logger.info(f"音频文件: {audio_name}, 大小: {audio_size} 字节, 时长: {audio_duration} 毫秒")

        # 2. 构建URL参数 - duration字段为毫秒整数
        url_params = {
            "appId": self.appid,
            "accessKeyId": self.access_key_id,
            "dateTime": date_time,
            "signatureRandom": self.signature_random,
            "fileSize": audio_size,
            "fileName": audio_name,
            "language": "autodialect",
            "duration": str(audio_duration)  # 音频时长（毫秒，整数字符串）
        }

        # 3. 生成签名（duration参数参与签名计算）
        signature = self.generate_signature(url_params)
        if not signature:
            raise Exception("签名生成失败，结果为空")

        # 4. 构建请求头
        headers = {
            "Content-Type": "application/octet-stream",
            "signature": signature
        }

        # 5. 构建最终请求URL
        encoded_params = []
        for k, v in url_params.items():
            encoded_key = urllib.parse.quote(k, safe='')
            encoded_v = urllib.parse.quote(str(v), safe='')
            encoded_params.append(f"{encoded_key}={encoded_v}")
        self.upload_url = f"{self.lfasr_host}{self.api_upload}?{'&'.join(encoded_params)}"

        # 6. 读取音频文件并发送POST请求
        with open(audio_file_path, "rb") as f:
            audio_data = f.read()

        try:
            response = requests.post(
                url=self.upload_url,
                headers=headers,
                data=audio_data,
                timeout=30,
                verify=False  # 测试环境关闭SSL验证
            )
            response.raise_for_status()
            result = response.json()
        except requests.exceptions.RequestException as e:
            raise Exception(f"上传请求网络失败：{str(e)}")

        # 8. 处理API业务错误
        if result.get("code") != "000000":
            raise Exception(
                f"上传失败（API错误）：\n"
                f"错误码：{result.get('code')}\n"
                f"错误描述：{result.get('descInfo', '未知错误')}\n"
                f"请求URL：{self.upload_url}\n"
                f"签名原始串：{self.last_base_string}\n"
                f"签名值：{self.last_signature}"
            )

        # 9. 上传成功，记录订单ID
        order_id = result["content"]["orderId"]
        logger.info(f"上传成功，订单ID: {order_id}")
        return order_id

    # 查询音频转写结果
    def get_transcribe_result(self, order_id: str) -> Union[int, str]:
        if not order_id:
            raise Exception("未获取到订单ID，无法查询转写结果")

        # 构建查询参数
        query_params = {
            "appId": self.appid,
            "accessKeyId": self.access_key_id,
            "dateTime": self._get_local_time_with_tz(),
            "ts": str(int(time.time())),  # 秒级时间戳
            "orderId": order_id,
            "signatureRandom": self.signature_random
        }

        # 生成查询签名
        query_signature = self.generate_signature(query_params)
        query_headers = {
            "Content-Type": "application/json",
            "signature": query_signature
        }

        # 构建查询URL
        encoded_query_params = []
        for k, v in query_params.items():
            encoded_key = urllib.parse.quote(k, safe='')
            encoded_v = urllib.parse.quote(str(v), safe='')
            encoded_query_params.append(f"{encoded_key}={encoded_v}")
        query_url = f"{self.lfasr_host}{self.api_get_result}?{'&'.join(encoded_query_params)}"

        try:
            response = requests.post(url=query_url, headers=query_headers, data=json.dumps({}), timeout=15, verify=False)
            response.raise_for_status()
        except requests.exceptions.RequestException as e:
            raise Exception(f"查询请求网络失败：{str(e)}")

        try:
            result = json.loads(response.text)
        except json.JSONDecodeError:
            raise Exception(f"查询响应非JSON数据：{response.text}")

        if result.get("code") != "000000":
            raise Exception(f"查询失败（API错误）：{result.get('descInfo', '未知错误')}")

        return result


# 实例化讯飞ASR客户端
xfyun_asr_client = XfyunAsrClient()


if __name__ == "__main__":
    audio_file_path = "/Users/wzq/Desktop/meeting/resource/downloaded_audio2.wav"  # WAV音频文件路径

    try:
        order_id = xfyun_asr_client.upload_audio(audio_file_path)
        while True:
            result = xfyun_asr_client.get_transcribe_result(order_id)
            result_status = result["content"]["orderInfo"]["status"]
            if result_status == 3:
                logger.info("音频转写中，等待10秒后再次查询…")
                logger.info(f"状态码={result_status}，描述={result.get('descInfo')}")
                time.sleep(10)
                continue
            if result_status == 4:
                break

        text = xfyun_asr_client.parse_order_result(result)
        logger.info(f"最终音频转写结果:\n{text}")
        with open("result.txt", "w") as f:
            f.write(text)
    except Exception as e:
        logger.error(f"程序执行失败: {e}")
